﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DomainModel
{
    public class SmartNoteDefaultModel : ISmartNoteModel
    {
        public SmartNoteDefaultModel()
        {
            mParentToChaildTag.Add(0, new List<int>());
        }

        public virtual void addTag(Tag tag, int pattentTagId)
        {
            if(tag.Id != 0)
            {
                throw new ApplicationException();
            }

            if (pattentTagId != 0 && mTags.Count(t => t.Id == pattentTagId) != 1)
            {
                throw new ApplicationException();
            }

            tag.Id = ++mLastTagId;

            mTags.Add(tag);
            mParentToChaildTag.Add(tag.Id, new List<int>());

            mParentToChaildTag[pattentTagId].Add(tag.Id);            
        }

        public virtual void addNote(Note note)
        {
            if (note.Id != 0)
            {
                throw new ApplicationException();
            }

            note.Id = ++mLastNoteId;

            mNotes.Add(note);            
            mNoteToTag.Add(note.Id, new List<int>());            
        }

        public virtual void addTagToNote(int tagId, int noteId)
        {
            if (mTags.Count(t => t.Id == tagId) != 1)
            {
                throw new ApplicationException();
            }

            if (mNotes.Count(n => n.Id == noteId) != 1)
            {
                throw new ApplicationException();
            }

            if (mNoteToTag[noteId].Contains(tagId))
            {
                throw new ApplicationException();
            }

            mNoteToTag[noteId].Add(tagId);
        }


        public virtual IList<Tag> getChildrenTags(int parentId)
        {
            if (parentId != 0 && mTags.Count(t => t.Id == parentId) != 1)
            {
                throw new ApplicationException();
            }

            return mTags.Where(t => mParentToChaildTag[parentId].Contains(t.Id)).ToList();            
        }

        public virtual IList<Note> findNotes(int tagId)
        {
            if (tagId == 0)
            {
                return mNotes;
            }

            if (mTags.Count(t => t.Id == tagId) != 1)
            {
                throw new ApplicationException();
            }

            List<Note> notes = new List<Note>();
            loadAllNotes(tagId, notes);
            return notes;
        }

        public virtual IList<Tag> getTags(int noteId)
        {
            if (mNotes.Count(n => n.Id == noteId) != 1)
            {
                throw new ApplicationException();
            }

            return mTags.Where(t => mNoteToTag[noteId].Contains(t.Id)).ToList();
        }


        public virtual void removeTagFromNote(int tagId, int noteId)
        {
            Tag tag = mTags.Where(n => n.Id == tagId).SingleOrDefault();

            if (tag == null)
            {
                throw new ApplicationException();
            }

            Note note = mNotes.Where(n => n.Id == noteId).SingleOrDefault();

            if (note == null)
            {
                throw new ApplicationException();
            }

            mNoteToTag[noteId].Remove(tagId);
        }

        public virtual void removeTag(int tagId)
        {
            Tag tag = mTags.Where(n => n.Id == tagId).SingleOrDefault();

            if (tag == null)
            {
                throw new ApplicationException();
            }

            if (mParentToChaildTag[tagId].Count() != 0)
            {
                throw new ApplicationException();
            }

            mParentToChaildTag.Remove(tagId);

            foreach (int noteId in mNoteToTag.Keys)
            {
                removeTagFromNote(tagId, noteId);
            }

            mTags.Remove(tag);
        }

        public virtual void removeNote(int noteId)
        {
            Note note = mNotes.Where(n => n.Id == noteId).SingleOrDefault();

            if (note == null)
            {
                throw new ApplicationException();
            }

            mNotes.Remove(note);
            mNoteToTag.Remove(note.Id);
        }


        private void loadAllNotes(int tagId, List<Note> notes)
        {
            notes.AddRange(mNoteToTag.Keys.Where(ni => mNoteToTag[ni].Contains(tagId)).Select(ni => mNotes.Single(n => n.Id == ni)));

            IList<Tag> tags = getChildrenTags(tagId);
            foreach (Tag tag in tags)
            {
                loadAllNotes(tag.Id, notes);
            }
        }

        private List<Tag> mTags = new List<Tag>();
        private List<Note> mNotes = new List<Note>();

        private Dictionary<int, List<int>> mParentToChaildTag = new Dictionary<int, List<int>>();
        private Dictionary<int, List<int>> mNoteToTag = new Dictionary<int, List<int>>();

        int mLastTagId = 0;
        int mLastNoteId = 0;
    };
}
